﻿/*
 * Copyright 2008 Lucifer Lu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Windows;
using System.Reflection;
using System.Collections.Generic;
using SilverChart.Core.Resources;
using SilverChart.Core.Log;

namespace SilverChart.Core {
    /// <summary>
    /// Delegate for the event which will be trigered when a assembly loaded.
    /// </summary>
    /// <param name="sender">AppDomainHelper instance</param>
    /// <param name="e">The event argument</param>
    public delegate void AssemblyLoadedDelegate(Object sender, AssemblyLoadedEventArg e);

    /// <summary>
    /// Delegate for the event which will be trigered when all assemblies in current xap file have been loaded.
    /// </summary>
    /// <param name="sender">AppDomainHelper instance</param>
    /// <param name="e">The event argument</param>
    public delegate void AllAssemlbiesHaveLoadedDelegate(Object sender, EventArgs e);

    /// <summary>
    /// Helper to help load assemblies in current xap file.
    /// 
    /// This class should be invoke when the application start. This class can scan all assemblies and start ServiceManager service.
    /// This class is a Singleton.
    /// </summary>
    public sealed class AppDomainHelper {
        IList<Assembly> assemblies;
        IDictionary<Assembly, IAssemblyHelper> assemblyHelpersMap;
        private static Object _instaceLock = new Object();
        private const String assemblyVersion = "0.0.0.0";

        private AppDomainHelper() {
            assemblies = new List<Assembly>();
            assemblyHelpersMap = new Dictionary<Assembly, IAssemblyHelper>();
        }

        public static AppDomainHelper Instance { get; private set; }

        public static void Initialize() {
            if (Instance == null) {
                lock (_instaceLock) {
                    if (Instance == null) {
                        Instance = new AppDomainHelper();
                        Instance.processAssemblies();
                    }
                }
            }
        }

        private void processAssemblies() {
            ProcessCurrentAssembly();
            String CurrentAssemblyName = this.GetType().Assembly.FullName;
            CurrentAssemblyName = CurrentAssemblyName.Substring(0, CurrentAssemblyName.IndexOf(","));
            foreach (AssemblyPart part in Deployment.Current.Parts) {
                String partSrc = part.Source;
                if (partSrc.ToLower().EndsWith(".dll")) {
                    String assemblyName = partSrc.Substring(0, partSrc.Length - 4);
                    if (CurrentAssemblyName == assemblyName) {
                        continue;
                    }

                    IAssemblyHelper helper = null;
                    try {
                        Type helpType = Type.GetType(GetAssemblyHelperClassName(assemblyName));
                        if (helpType == null) continue;

                        helper = Activator.CreateInstance(helpType) as IAssemblyHelper;
                    }
                    catch {
                    }

                    AddAssembly(helper);
                }
            }

            if (AllAssembliesHaveLoaded != null) {
                try {
                    AllAssembliesHaveLoaded.Invoke(this, new EventArgs());
                }
                catch {
                }
            }
        }

        private void ProcessCurrentAssembly() {
            AssemblyHelper helper = new AssemblyHelper();
            AddAssembly(helper);
            helper.ProcessFrameworkAssembly();
        }

        private void AddAssembly(IAssemblyHelper helper) {
            if (helper != null) {
                Assembly assembly = helper.GetType().Assembly;
                assemblies.Add(assembly);
                assemblyHelpersMap.Add(helper.GetType().Assembly, helper);

                if (AssemblyLoaded != null) {
                    try {
                        AssemblyLoaded.Invoke(this, new AssemblyLoadedEventArg(assembly));
                    }
                    catch {
                    }
                }
                try {
                    helper.Loaded(this);
                }
                catch {
                }
            }
        }

        private String GetAssemblyVersion(String assemblyName) {
            Uri uri = new Uri("/" + assemblyName + ";component/assembly.version", UriKind.Relative);
            String verString = ResourcesHelper.Instance.ReadResourcesFromURL(uri);
            if (verString == null) {
                verString = assemblyVersion;
            }
            else {
                try {
                    Version version = new Version(verString.Trim());
                }
                catch {
                    verString = assemblyVersion;
                }
            }
            return verString;
        }

        private String GetAssemblyHelperClassName(String assemblyName) {
            return assemblyName + ".AssemblyHelper" + "," + assemblyName + ", Version=" + GetAssemblyVersion(assemblyName);
        }

        public Int32 AssemblyCount {
            get {
                return assemblies.Count;
            }
        }

        public IAssemblyHelper GetHelper(Assembly assembly) {
            IAssemblyHelper helper = null;
            assemblyHelpersMap.TryGetValue(assembly, out helper);
            return helper;
        }

        public event AssemblyLoadedDelegate AssemblyLoaded;
        public event AllAssemlbiesHaveLoadedDelegate AllAssembliesHaveLoaded;
    }
}
